package cn.metona.utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public final class EntityUtils {

    private EntityUtils() {
        throw new UnsupportedOperationException("Utility class");
    }

    // 缓存类的字段信息，减少反射调用开销
    private static final Map<Class<?>, List<Field>> FIELD_CACHE = new ConcurrentHashMap<>();

    /**
     * 复制源对象的属性到目标对象。
     *
     * @param source 源对象
     * @param target 目标对象
     * @param <T>    源对象类型
     * @param <U>    目标对象类型
     * @throws RuntimeException 如果复制失败
     */
    public static <T, U> void copyProperties(T source, U target) {
        Objects.requireNonNull(source, "Source object must not be null");
        Objects.requireNonNull(target, "Target object must not be null");

        List<Field> sourceFields = getFields(source.getClass());
        List<Field> targetFields = getFields(target.getClass());

        for (Field sourceField : sourceFields) {
            sourceField.setAccessible(true);
            String fieldName = sourceField.getName();
            Object fieldValue = getFieldValue(source, sourceField);

            targetFields.stream()
                    .filter(targetField -> targetField.getName().equals(fieldName))
                    .findFirst()
                    .ifPresent(targetField -> {
                        try {
                            targetField.setAccessible(true);
                            setFieldValue(target, targetField, fieldValue);
                        } catch (Exception e) {
                            throw new RuntimeException("Failed to set field value: " + fieldName, e);
                        }
                    });
        }
    }

    /**
     * 合并两个实体的属性，将非空字段从源对象复制到目标对象。
     *
     * @param source 源对象
     * @param target 目标对象
     * @param <T>    源对象类型
     * @param <U>    目标对象类型
     */
    public static <T, U> void mergeEntities(T source, U target) {
        Objects.requireNonNull(source, "Source object must not be null");
        Objects.requireNonNull(target, "Target object must not be null");

        List<Field> sourceFields = getFields(source.getClass());
        List<Field> targetFields = getFields(target.getClass());

        for (Field sourceField : sourceFields) {
            sourceField.setAccessible(true);
            String fieldName = sourceField.getName();
            Object fieldValue = getFieldValue(source, sourceField);

            if (fieldValue != null) {
                targetFields.stream()
                        .filter(targetField -> targetField.getName().equals(fieldName))
                        .findFirst()
                        .ifPresent(targetField -> {
                            try {
                                targetField.setAccessible(true);
                                setFieldValue(target, targetField, fieldValue);
                            } catch (Exception e) {
                                throw new RuntimeException("Failed to set field value: " + fieldName, e);
                            }
                        });
            }
        }
    }

    /**
     * 将实体对象转换为 Map<String, Object>。
     *
     * @param entity 实体对象
     * @return 包含字段名和字段值的 Map
     */
    public static Map<String, Object> toMap(Object entity) {
        Objects.requireNonNull(entity, "Entity must not be null");

        return getFields(entity.getClass()).stream()
                .collect(Collectors.toMap(
                        Field::getName,
                        field -> {
                            field.setAccessible(true);
                            return getFieldValue(entity, field);
                        }
                ));
    }

    /**
     * 将 Map<String, Object> 转换为实体对象。
     *
     * @param map   Map 数据
     * @param clazz 目标实体类
     * @param <T>   实体类型
     * @return 实体对象
     */
    public static <T> T fromMap(Map<String, Object> map, Class<T> clazz) {
        Objects.requireNonNull(map, "Map must not be null");
        Objects.requireNonNull(clazz, "Target class must not be null");

        try {
            T instance = clazz.getDeclaredConstructor().newInstance();
            List<Field> fields = getFields(clazz);

            for (Field field : fields) {
                field.setAccessible(true);
                Object value = map.get(field.getName());
                if (value != null) {
                    setFieldValue(instance, field, value);
                }
            }

            return instance;
        } catch (Exception e) {
            throw new RuntimeException("Failed to create instance from map", e);
        }
    }

    /**
     * 验证实体对象的字段是否符合规则。
     *
     * @param entity    实体对象
     * @param predicate 验证规则
     * @return 如果所有字段都符合规则，则返回 true；否则返回 false
     */
    public static boolean validateEntity(Object entity, Predicate<Object> predicate) {
        Objects.requireNonNull(entity, "Entity must not be null");
        Objects.requireNonNull(predicate, "Predicate must not be null");

        return getFields(entity.getClass()).stream()
                .allMatch(field -> {
                    field.setAccessible(true);
                    return predicate.test(getFieldValue(entity, field));
                });
    }

    /**
     * 深拷贝实体对象。
     *
     * @param entity 实体对象
     * @param clazz  实体类
     * @param <T>    实体类型
     * @return 深拷贝后的实体对象
     */
    public static <T> T deepCopy(T entity, Class<T> clazz) {
        Objects.requireNonNull(entity, "Entity must not be null");
        Objects.requireNonNull(clazz, "Target class must not be null");

        return fromMap(toMap(entity), clazz);
    }

    /**
     * 获取类的所有字段（包括父类字段）。
     *
     * @param clazz 类
     * @return 字段列表
     */
    private static List<Field> getFields(Class<?> clazz) {
        return FIELD_CACHE.computeIfAbsent(clazz, k -> {
            List<Field> fields = new ArrayList<>();
            Class<?> currentClass = clazz;
            while (currentClass != null) {
                fields.addAll(Arrays.asList(currentClass.getDeclaredFields()));
                currentClass = currentClass.getSuperclass();
            }
            return fields;
        });
    }

    /**
     * 获取字段的值。
     *
     * @param entity 实体对象
     * @param field  字段
     * @return 字段值
     */
    private static Object getFieldValue(Object entity, Field field) {
        try {
            field.setAccessible(true);
            return field.get(entity);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Failed to get field value: " + field.getName(), e);
        }
    }

    /**
     * 设置字段的值。
     *
     * @param entity 实体对象
     * @param field  字段
     * @param value  字段值
     */
    private static void setFieldValue(Object entity, Field field, Object value) {
        try {
            field.setAccessible(true);
            if (field.getType().isPrimitive() || value.getClass().equals(field.getType())) {
                field.set(entity, value);
            } else {
                // 支持基本类型和常见类型的转换
                if (field.getType() == Integer.class || field.getType() == int.class) {
                    field.set(entity, Integer.valueOf(value.toString()));
                } else if (field.getType() == Long.class || field.getType() == long.class) {
                    field.set(entity, Long.valueOf(value.toString()));
                } else if (field.getType() == Double.class || field.getType() == double.class) {
                    field.set(entity, Double.valueOf(value.toString()));
                } else if (field.getType() == Boolean.class || field.getType() == boolean.class) {
                    field.set(entity, Boolean.valueOf(value.toString()));
                } else {
                    field.set(entity, value);
                }
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Failed to set field value: " + field.getName(), e);
        }
    }

    /**
     * 根据字段名过滤字段。
     *
     * @param entity    实体对象
     * @param fieldName 字段名
     * @return 字段值
     */
    public static Object getFieldByName(Object entity, String fieldName) {
        Objects.requireNonNull(entity, "Entity must not be null");
        Objects.requireNonNull(fieldName, "Field name must not be null");

        return getFields(entity.getClass()).stream()
                .filter(field -> field.getName().equals(fieldName))
                .findFirst()
                .map(field -> getFieldValue(entity, field))
                .orElseThrow(() -> new RuntimeException("Field not found: " + fieldName));
    }

    /**
     * 根据字段类型过滤字段。
     *
     * @param entity    实体对象
     * @param fieldType 字段类型
     * @return 字段值列表
     */
    public static List<Object> getFieldsByType(Object entity, Class<?> fieldType) {
        Objects.requireNonNull(entity, "Entity must not be null");
        Objects.requireNonNull(fieldType, "Field type must not be null");

        return getFields(entity.getClass()).stream()
                .filter(field -> field.getType().equals(fieldType))
                .map(field -> getFieldValue(entity, field))
                .collect(Collectors.toList());
    }

    /**
     * 为实体对象的空字段设置默认值。
     *
     * @param entity       实体对象
     * @param defaultValue 默认值
     */
    public static void setDefaultValues(Object entity, Object defaultValue) {
        Objects.requireNonNull(entity, "Entity must not be null");

        getFields(entity.getClass()).forEach(field -> {
            field.setAccessible(true);
            try {
                if (getFieldValue(entity, field) == null) {
                    setFieldValue(entity, field, defaultValue);
                }
            } catch (Exception e) {
                throw new RuntimeException("Failed to set default value for field: " + field.getName(), e);
            }
        });
    }

    /**
     * 比较两个实体对象的字段值是否相同。
     *
     * @param entity1 实体对象 1
     * @param entity2 实体对象 2
     * @return 如果所有字段值相同，则返回 true；否则返回 false
     */
    public static boolean compareEntities(Object entity1, Object entity2) {
        Objects.requireNonNull(entity1, "Entity 1 must not be null");
        Objects.requireNonNull(entity2, "Entity 2 must not be null");

        if (!entity1.getClass().equals(entity2.getClass())) {
            return false;
        }

        return getFields(entity1.getClass()).stream()
                .allMatch(field -> {
                    field.setAccessible(true);
                    return Objects.equals(getFieldValue(entity1, field), getFieldValue(entity2, field));
                });
    }

    /**
     * 通过注解获取字段值。
     *
     * @param entity        实体对象
     * @param annotationClass 注解类
     * @return 包含注解字段值的 Map
     */
    public static Map<String, Object> getFieldsByAnnotation(Object entity, Class<? extends java.lang.annotation.Annotation> annotationClass) {
        Objects.requireNonNull(entity, "Entity must not be null");
        Objects.requireNonNull(annotationClass, "Annotation class must not be null");

        return getFields(entity.getClass()).stream()
                .filter(field -> field.isAnnotationPresent(annotationClass))
                .collect(Collectors.toMap(
                        Field::getName,
                        field -> getFieldValue(entity, field)
                ));
    }

    /**
     * 批量复制实体对象。
     *
     * @param sources 源对象列表
     * @param targetClass 目标类
     * @param <T> 源对象类型
     * @param <U> 目标对象类型
     * @return 目标对象列表
     */
    public static <T, U> List<U> copyList(List<T> sources, Class<U> targetClass) {
        Objects.requireNonNull(sources, "Sources must not be null");
        Objects.requireNonNull(targetClass, "Target class must not be null");

        return sources.stream()
                .map(source -> {
                    try {
                        U target = targetClass.getDeclaredConstructor().newInstance();
                        copyProperties(source, target);
                        return target;
                    } catch (Exception e) {
                        throw new RuntimeException("Failed to copy entity", e);
                    }
                })
                .collect(Collectors.toList());
    }
}
